package main

import (
	"encoding/json"
	"fmt"
	"github.com/baker-yuan-connect/botgo/dto"
	myHttp "github.com/baker-yuan-connect/botgo/http"
	"github.com/baker-yuan-connect/botgo/logic"
	"github.com/gorilla/websocket"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// StringStrip 去掉字符串中所有的空格
func StringStrip(input string) string {
	if input == "" {
		return ""
	}
	return strings.Join(strings.Fields(input), "")
}

// GetContent 解析返回值
// eg：<@!9617602541247323354> 呵呵
func GetContent(input string)  string {
	reg := regexp.MustCompile(`<@!\d+>`)
	prefixList := reg.FindAllString(input, -1)
	if len(prefixList) == 0 {
		return ""
	}
	result := strings.Replace(input, prefixList[0], "", 1)
	return result
}


// 签到业务类 内存实现
var sign = logic.SignByRedis{}

// Heartbeat 上报心跳 非阻塞方法
// https://bot.q.qq.com/wiki/develop/api/gateway/reference.html
func Heartbeat(mySocket *websocket.Conn, heartbeatTnterval int) {
	done := make(chan struct{})
	// 协程
	go func() {
		defer close(done)
		ticker := time.NewTicker(5 * time.Second)
		defer ticker.Stop()
		for range ticker.C {
			log.Println("开始发送心跳包")
			heartbeat := &dto.WSPayload{
				WSPayloadBase: dto.WSPayloadBase{
					OPCode: dto.WSHeartbeat,
				},
				Data: heartbeatTnterval,
			}
			payload, _ := json.Marshal(heartbeat)
			err := mySocket.WriteMessage(websocket.TextMessage, []byte(payload))
			if err != nil {
				log.Println("发送心跳失败:", err)
				//initSocket()
				return
			}
			log.Println("发送心跳包结束")
		}
	}()
}

// 处理@消息
func handAtMessage(data interface{}, authToken string) {
	// todo 能实现，不够优雅，后面找下原因
	pdJson, _ := json.Marshal(data)
	rvMsg := &dto.Message{}
	json.Unmarshal(pdJson, &rvMsg)

	//rvMsg := payload.Data.(dto.Message)

	log.Println("cmd", rvMsg.Content)
	log.Println("bot", rvMsg.Author.Bot)
	//if true == rvMsg.Author.Bot {
	//	fmt.Println("bot", rvMsg.Author.Bot)
	//	return
	//}
	content := GetContent(rvMsg.Content)
	splitList := strings.Split(strings.TrimSpace(content), " ")
	log.Println("splitList:", splitList)

	cmd := StringStrip(splitList[0])
	var message = ""
	if len(cmd) == 0 {
		message = "\n我是签到机器人baker\n" +
			dto.Line + "\n" +
			"今日签到@我输入《1》\n" +
			"查看本月签到@我输入《2》\n" +
			"补签@我输入《3+空格+日期》\n"+
			dto.Line + "\n"
	} else if "1" == cmd {
		doSign, err := logic.DoSign(sign, rvMsg.Author.ID)
		if doSign {
			message = "\n今日签到成功～～～"
		} else if err != nil {
			message = "\n签到失败，" + err.Error()
		} else {
			message = "\n系统异常，请稍后再试～～～"
		}
	} else if "2" == cmd {
		signInfo := logic.SignInfo(sign, rvMsg.Author.ID)
		message = "\n本月连续签到情况：" + signInfo
	} else if "3" == cmd {
		if len(splitList) != 2 {
			message = "\n输入错误，无法签到了～～："
		} else {
			offset := StringStrip(splitList[1])
			iOffset, _ := strconv.Atoi(offset)
			result, err := logic.CompensateSign(sign, rvMsg.Author.ID, iOffset - 1)
			if result ==false && err != nil {
				message = err.Error()
			} else if result == true {
				message = "\n今日补签成功，一天只有一次补签机会～～～"
			} else {
				message = "\n系统异常，请稍后再试～～～"
			}

		}
	} else {
		log.Printf("触发智能回复...")
		// 智能回复
		message = strings.Replace(content,"吗", "", -1)
		message = strings.Replace(message,"?", "！", -1)
		message = strings.Replace(message,"？", "！", -1)
		message = "\n" + message
	}
	myHttp.SendMessage(rvMsg.ChannelID, rvMsg.ID, message, authToken)
}

var authToken = fmt.Sprintf("%v.%s", dto.AppId, dto.Token)
var mySocket *websocket.Conn

// 初始化websocket连接
func initSocket() {
	// 1、连接websocket
	ws, _, err := websocket.DefaultDialer.Dial(dto.UrlStr, nil)
	if err != nil {
		log.Fatal("websocket连接异常:", err)
	}
	//defer ws.Close()
	log.Printf("连接建立结束...")

	// 2、权限校验
	// {"op":2,"d":{"token":"102033770.NPlaif1Jl0snX11978OU4BmYVacOEcp0","intents":1409290755,"shard":[0,1],"properties":{}}}
	message := &dto.WSPayload{
		Data: &dto.WSIdentityData{
			// token
			Token:   authToken,
			// 事件订阅 intents
			// https://bot.q.qq.com/wiki/develop/api/gateway/intents.html
			Intents: 1409290755,
			// 分片
			// https://bot.q.qq.com/wiki/develop/api/gateway/shard.html
			//Shard: []uint32{
			//	0,
			//	1,
			//},
		},
	}
	message.OPCode = dto.WSIdentity
	m, _ := json.Marshal(message)
	err = ws.WriteMessage(websocket.TextMessage, []byte(m))
	if err != nil {
		log.Println("鉴权失败:", err)
		return
	}
	mySocket = ws
}

// 处理消息回掉 阻塞方法
func listenMessage()  {
	heartbeatTnterval := 41250
	for {
		// 从接收服务端message
		// 连接成功 {"d":{"heartbeat_interval":41250},"op":10}
		// 鉴权成功 {"op":0,"s":1,"t":"READY","d":{"version":1,"session_id":"18f8e37c-c68a-41d9-a1b7-4752d9e9100b","user":{"id":"961760254123354","username":"rabbit-测试中","bot":true,"status":1},"shard":[0,1]}}
		// 心跳发送成功 {"op": 11}
		_, message, err := mySocket.ReadMessage()
		if err != nil {
			log.Println("接收服务端消息失败:", err)
			initSocket()
			continue
		}
		log.Printf("收到消息: %s", message)
		if message == nil {
			continue
		}
		// 根据不同的消息类型处理不同的消息
		payload := &dto.WSPayload{}
		if err := json.Unmarshal(message, &payload); err != nil {
			log.Println("json解析失败:", err)
			continue
		}
		payload.RawMessage = message

		log.Printf("oPCode：%d,op：%s", payload.WSPayloadBase.OPCode, dto.OpMeans[payload.WSPayloadBase.OPCode])

		if payload.WSPayloadBase.OPCode == dto.WSHeartbeatAck {
			log.Println("发送心跳成功:", string(message))
		}
		if payload.WSPayloadBase.OPCode == dto.WSHello {
			pdJson, _ := json.Marshal(payload.Data)
			rvMsg := &dto.WSHelloData{}
			json.Unmarshal(pdJson, &rvMsg)
			heartbeatTnterval = rvMsg.HeartbeatInterval
			log.Println("网关下发的第一条消息:", string(message))
		}
		if payload.WSPayloadBase.OPCode == dto.WSDispatchEvent && payload.WSPayloadBase.Type == "READY" {
			log.Println("鉴权成功:", string(message))
			// 启动定时器，定时发送心跳包
			Heartbeat(mySocket, heartbeatTnterval)
		}
		if payload.WSPayloadBase.OPCode == dto.WSDispatchEvent {
			// @消息
			// https://bot.q.qq.com/wiki/develop/api/gateway/message.html#at-message-create-intents-public-guild-messages
			if payload.WSPayloadBase.Type == "AT_MESSAGE_CREATE" {
				handAtMessage(payload.Data, authToken)
			}
		}
	}
}

// 文档：https://bot.q.qq.com/wiki/develop/api/
// Websocket接入：https://bot.q.qq.com/wiki/develop/api/gateway/reference.html
func main() {
	log.Printf("start...")
	// 初始化websocket
	initSocket()
	// 处理消息监听（阻塞）
	listenMessage()
	// 释放资源
	defer mySocket.Close()
	log.Println("end...")
}